package com.mytijian.openapi.adapter.dianping.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.mytijian.openapi.adapter.dianping.client.DzopenUtils;
import com.mytijian.openapi.adapter.dianping.consts.DzopenConsts;
import com.mytijian.openapi.adapter.dianping.exceptions.DzopenErrorInfo;
import com.mytijian.openapi.adapter.dianping.exceptions.DzopenException;
import com.mytijian.openapi.adapter.dianping.inner.param.DzConsumeParam;
import com.mytijian.openapi.adapter.dianping.model.DzopenOrderRel;
import com.mytijian.openapi.adapter.dianping.param.DzConsumestatusResult;
import com.mytijian.openapi.adapter.dianping.param.DzopenOrderRelQuery;
import com.mytijian.openapi.adapter.dianping.result.DzCosumestatusParam;
import com.mytijian.openapi.adapter.dianping.result.DzResult;
import com.mytijian.openapi.adapter.dianping.service.DzOpenConsumeService;
import com.mytijian.openapi.adapter.dianping.service.DzopenOrderRelService;
import com.mytijian.openapi.core.client.IOpenAPIClient;
import com.mytijian.openapi.core.client.result.OrderInfo;
import com.mytijian.openapi.core.client.result.OrderListResult;
import com.mytijian.openapi.core.model.AppConfig;
import com.mytijian.openapi.core.service.OpenApiAppConfigService;
import com.mytijian.utils.DateUtils;

@Service("dzOpenConsumeService")
public class DzOpenConsumeServiceImpl implements DzOpenConsumeService {

	private Logger logger = LoggerFactory.getLogger(getClass());

	@Value("${dzopen.appkey}")
	private String appkey;

	@Value("${dzopen.signkey}")
	private String signkey;

	@Autowired
	private DzopenOrderRelService dzopenOrderRelService;

	@Autowired
	private OpenApiAppConfigService openApiAppConfigService;

	@Autowired
	private DzopenShopAuthServiceImpl dzopenShopAuthServiceImpl;

	private final int openapiMaxOrderCount = 50;

	@Override
	public DzConsumestatusResult queryOrderCosumestatus(DzCosumestatusParam param) {
		DzopenOrderRelQuery query = new DzopenOrderRelQuery();

		query.setDzOrderId(param.getOrderId());
		DzopenOrderRel rel = dzopenOrderRelService.getOrderRel(query);

		if (rel == null) {
			throw new DzopenException(DzopenErrorInfo.ORDER_QUERY_NOT_FOUND);
		}

		DzConsumestatusResult result = new DzConsumestatusResult();

		result.setOrder_id(param.getOrderId());
		Integer status = rel.getOrderConsumeStatus();

		status = status > 2 ? 2 : status;

		result.setConsume_status(String.valueOf(status));

		return result;
	}

	@Autowired
	private IOpenAPIClient client;

	@Override
	public void consumeExamDateIsYesterday() {

		final Date yesterday = new Date(System.currentTimeMillis() - 86400000L);

		DzopenOrderRelQuery query = new DzopenOrderRelQuery();
		query.setOrderConsumeStatus(DzopenConsts.OrderConsumeStatus.normal);
		query.setExamDate(DateUtils.format(yesterday));
		List<DzopenOrderRel> orders = dzopenOrderRelService.listOrderRels(query);

		logger.info("[美团点评][订单核销] 开始核销{}体检的订单。总计{}条", DateUtils.format(yesterday), orders.size());

		Map<String, DzopenOrderRel> orderNumRelMap = new HashMap<>();
		
		for (int offerSet = 0, pageSize = openapiMaxOrderCount; offerSet < orders.size(); offerSet += pageSize) {
			List<DzopenOrderRel> subOrderPage;

			if ((offerSet + pageSize)>orders.size()){
				subOrderPage = orders.subList(offerSet, orders.size());
			}else {
				subOrderPage = orders.subList(offerSet, offerSet + pageSize);
			}
			List<String> subOrderNums = new ArrayList<>();

			subOrderPage.forEach(rel -> {
				subOrderNums.add(rel.getOrderNum());
				orderNumRelMap.put(rel.getOrderNum(), rel);
			});

			OrderListResult openlist = getOpenAPIOrders(subOrderNums);

			List<OrderInfo> orderInfos = openlist.getOrders();

			consumeAll(yesterday, orderNumRelMap, orderInfos);

		}
		logger.info("[美团点评][订单核销] 核销成功。总计{}条", orders.size());
	}

	private void consumeAll(final Date consumeDate, Map<String, DzopenOrderRel> orderNumRelMap,
			List<OrderInfo> orderInfos) {
		for (OrderInfo orderInfo : orderInfos) {
			// 根据订单状态调用核销接口
			try {
				DzopenOrderRel orderRel = orderNumRelMap.get(orderInfo.getOrderNum());
				orderRel = syncOpenapiOrderStatus(consumeDate, orderInfo, orderRel);

				if (needConsume(consumeDate, orderInfo, orderRel)) {
					doConsume(orderRel);
				}
			} catch (Exception e) {
				logger.error("!!! [美团点评] 订单核销失败 订单信息 {}", orderInfo);
			}
		}
	}

	private boolean needConsume(final Date consumeDate, OrderInfo orderInfo, DzopenOrderRel orderRel) {
		Date examDate = DateUtils.parse(orderInfo.getExamDate());
		if (examDate.getTime() > consumeDate.getTime()) {
			logger.warn("[美团点评][订单核销] 订单信息{},由于体检时间在本次所要核销的时间'{}'之后。跳过本订单核销。",
					ToStringBuilder.reflectionToString(orderRel), DateUtils.format(consumeDate));
			return false;
		}
		if (orderRel.getOrderConsumeStatus() != DzopenConsts.OrderConsumeStatus.normal) {
			logger.warn("[美团点评][订单核销] 订单信息{}, 订单状态为不可撤销，跳过本次核销。", ToStringBuilder.reflectionToString(orderRel),
					DateUtils.format(consumeDate));
			return false;
		}

		return true;
	}

	private OrderListResult getOpenAPIOrders(List<String> orderNums) {
		// TODO 总体业务量小 循环内查询openConfig，以避免处理过程中accessToken失效
		// 调用开放平台所有错误可认为调用失败
		AppConfig config = openApiAppConfigService.getConfig(DzopenConsts.AppName);
		OrderListResult openlist = client.getOrderStatus(config, orderNums);

		if (openlist == null || (StringUtils.isNotEmpty(openlist.getErrCode()))) {
			logger.info("[美团点评][订单核销] 调用开放平台失败 返回结果: {}", ToStringBuilder.reflectionToString(openlist));
		}
		return openlist;
	}

	private DzopenOrderRel syncOpenapiOrderStatus(final Date consumeDate, OrderInfo orderInfo,
			DzopenOrderRel orderRel) {
		if (!checkOrderStatus(orderInfo.getOrderStatus())) {
			orderRel.setOrderConsumeStatus(DzopenConsts.OrderConsumeStatus.canceled);
			dzopenOrderRelService.updateDzopenOrderRel(orderRel);
			logger.warn("[美团点评][订单核销] 订单状态不一致 关系表信息{}，开放平台订单状态:{}。已修改。", ToStringBuilder.reflectionToString(orderRel),
					orderInfo.getOrderStatus());
		}

		if (!orderInfo.getExamDate().equals(DateUtils.format(orderRel.getExamDate()))) {
			orderRel.setExamDate(DateUtils.parse(orderInfo.getExamDate()));
			dzopenOrderRelService.updateDzopenOrderRel(orderRel);

			logger.warn("[美团点评][订单核销] 体检日期不一致 关系表信息{}，开放平台体检日:{}。已修改关系表体检日期。",
					ToStringBuilder.reflectionToString(orderRel), orderInfo.getExamDate());
		}
		return orderRel;
	}

	@Override
	public void doConsume(DzopenOrderRel orderRel) {
		DzConsumeParam dcp = new DzConsumeParam();
		dcp.setAppKey(appkey);
		dcp.setOrderId(orderRel.getDzOrderId());
		dcp.setTimestamp(DateUtils.format(DateUtils.YYYY_MM_DD_HMS, new Date()));
		dcp.setFormat("json");
		dcp.setSignMethod("MD5");
		dcp.setV("1");

		dcp.setSession(dzopenShopAuthServiceImpl.getSession(orderRel.getHospitalId()));

		dcp.setAppShopId(String.valueOf(orderRel.getHospitalId()));
		DzResult<?> result = DzopenUtils.sendRequest(DzopenConsts.Apis.Consume,signkey, dcp);
		if (DzopenConsts.SuccessCode.equals(result.getCode())) {
			orderRel.setOrderConsumeStatus(DzopenConsts.OrderConsumeStatus.consumed);
			this.dzopenOrderRelService.updateDzopenOrderRel(orderRel);
		} else {
			logger.error("[美团点评][核销失败] 订单信息:{} 接口返回:{}", ToStringBuilder.reflectionToString(orderRel),
					ToStringBuilder.reflectionToString(result));
		}
	}

	/**
	 * 所有非撤销订单均可核销
	 * @param orderStatus
	 * @return
	 */
	private boolean checkOrderStatus(String orderStatus) {

		return !"cancel".equals(orderStatus);
	}

}
